from docx import Document
from docx.shared import Pt, Cm
import requests
from flask import Flask, request, jsonify, send_from_directory
import uuid
from Utils.doc_parser import parse_document
from Utils.llm_client import build_deepseek_prompt
import json
import os
from PyPDF2 import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import io
from Utils.saveDataAndGet import save_analysis_data, get_analysis_data

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['CALIBRATED_FOLDER'] = 'calibrated'
DEEPSEEK_API_KEY = "sk-66d7155f1d5242baa7f9d93fefd6ce5a"  # API_KEY


# 上传文件接口
@app.route('/api/upload', methods=['POST'])
def handle_upload():
    if 'file' not in request.files:
        return jsonify({"code": 400, "message": "No file"}), 400

    file = request.files['file']
    file_id = f"{uuid.uuid4().hex}{os.path.splitext(file.filename)[1]}"
    file.save(os.path.join(app.config['UPLOAD_FOLDER'], file_id))

    return jsonify({
        "code": 200,
        "data": {"fileId": file_id}
    })


# 解析文件并调用大模型分析接口
@app.route('/api/analyze', methods=['POST'])
def analyze_document():
    try:
        # 获取文件信息
        file_id = request.json.get('fileId')
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], file_id)

        # 解析文档内容（基于网页7、17）
        parsed_data = parse_document(filepath)
        print("parsed_data", parsed_data)

        # 调用 DeepSeek API（基于网页49、32）
        headers = {
            "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
            "Content-Type": "application/json"
        }
        payload = {
            "model": "deepseek-chat",
            "messages": [{
                "role": "user",
                "content": build_deepseek_prompt(parsed_data)
            }],
            "temperature": 0.1,
            "response_format": {"type": "json_object"}
        }

        # 调用DeepSeek API
        response = requests.post(
            "https://api.deepseek.com/chat/completions",
            headers=headers,
            json=payload
        )
        print(response.json())

        # 处理API响应
        if response.status_code != 200:
            return jsonify({"code": 500, "message": "DeepSeek API请求失败"}), 500

        # 解析返回内容
        result_str = response.json()['choices'][0]['message']['content']
        try:
            result = json.loads(result_str)

            # 数据有效性校验（新增）
            if 'structure' not in result or 'errors' not in result:
                raise ValueError("返回结构不完整")

                # 转换评分类型（新增）
            try:
                structure_score = int(result['structure'].get('score', 0))
            except ValueError:
                structure_score = 0
            save_analysis_data(file_id, result)  # 保存文件id
            # 构建标准化响应（关键修改点）
            return jsonify({
                "code": 200,
                "data": {
                    "structure": {
                        "titleStatus": "完整" if structure_score > 60 else "不完整",
                        "chapterIntegrity": structure_score,
                        "missingSections": result['structure'].get('missing_sections', [])
                    },
                    "errors": [
                        {
                            "type": error.get('type', '未知错误'),
                            "description": error.get('description', ''),
                            "page": str(error.get('page', '全局'))  # 统一转换为字符串
                        }
                        for error in result.get('errors', [])
                    ]
                }
            })

        except json.JSONDecodeError:
            return jsonify({"code": 500, "message": "响应解析失败"}), 500
        except Exception as e:
            return jsonify({"code": 500, "message": f"结果处理错误: {str(e)}"}), 500

    except Exception as e:
        return jsonify({"code": 500, "message": str(e)}), 500


# 格式校准接口
@app.route('/api/calibrate', methods=['POST'])
def calibrate_document():
    try:
        # 获取分析结果（需持久化存储）
        file_id = request.json.get('fileId')
        analysis_data = get_analysis_data(file_id)
        print("analysis_data", analysis_data)

        if not analysis_data:
            return jsonify({
                "code": 404,
                "message": "未找到分析结果"
            }), 404

        # 文件路径处理
        src_path = os.path.join(app.config['UPLOAD_FOLDER'], file_id)
        calibrated_id = f"calibrated_{file_id}"
        dest_path = os.path.join(app.config['CALIBRATED_FOLDER'], calibrated_id)

        # 根据文件类型选择处理器
        if src_path.endswith('.docx'):
            calibrate_word_doc(src_path, dest_path, analysis_data)
        elif src_path.endswith('.pdf'):
            calibrate_pdf_doc(src_path, dest_path, analysis_data)
        else:
            return jsonify({"code": 400, "message": "不支持的文件格式"}), 400

        return jsonify({
            "code": 200,
            "data": {"calibratedId": calibrated_id}
        })

    except Exception as e:
        app.logger.error(f"校准失败: {str(e)}")
        return jsonify({"code": 500, "message": f"校准失败: {str(e)}"}), 500


# 下载接口
@app.route('/api/download/<file_id>')  # 用于下载校准后的文件
def download_file(file_id):
    return send_from_directory(
        app.config['CALIBRATED_FOLDER'],
        file_id,
        as_attachment=True
    )


def calibrate_pdf_doc(src_path, dest_path, analysis_data):
    """
    校准PDF文档的格式
    params:
        src_path: 源PDF文件路径
        dest_path: 目标PDF文件路径
        analysis_data: 分析结果数据
    """
    try:
        # 读取原始PDF
        reader = PdfReader(src_path)
        writer = PdfWriter()

        # 注册字体（使用更多的中文字体选项）
        fonts = {
            'SimSun': 'simsun.ttc',  # 宋体
            'SimHei': 'simhei.ttf',  # 黑体
            'KaiTi': 'simkai.ttf',  # 楷体
            'FangSong': 'simfang.ttf',  # 仿宋
        }

        registered_fonts = []
        for font_name, font_file in fonts.items():
            try:
                pdfmetrics.registerFont(TTFont(font_name, font_file))
                registered_fonts.append(font_name)
            except Exception as font_error:
                app.logger.warning(f"字体 {font_name} 注册失败: {str(font_error)}")

        if not registered_fonts:
            app.logger.warning("所有中文字体注册失败，将使用默认字体")

        # 处理每一页
        for page_num in range(len(reader.pages)):
            try:
                # 获取原始页面
                original_page = reader.pages[page_num]
                page_width = float(original_page.mediabox.width)
                page_height = float(original_page.mediabox.height)

                # 创建新的页面
                packet = io.BytesIO()
                can = canvas.Canvas(packet, pagesize=(page_width, page_height))

                # 获取当前页的错误
                page_errors = [
                    error for error in analysis_data.get('errors', [])
                    if str(error.get('page', '')) == str(page_num + 1)
                ]

                if page_errors:
                    # 按错误类型优先级排序处理
                    page_errors.sort(key=lambda x: get_error_priority(x.get('type', '')))

                    for error in page_errors:
                        apply_pdf_correction(can, error, page_width, page_height, registered_fonts)

                    can.save()
                    packet.seek(0)

                    try:
                        new_pdf = PdfReader(packet)
                        if len(new_pdf.pages) > 0:
                            new_page = new_pdf.pages[0]
                            original_page.merge_page(new_page)
                    except Exception as merge_error:
                        app.logger.error(f"页面合并失败: {str(merge_error)}")

                writer.add_page(original_page)

            except Exception as page_error:
                app.logger.error(f"处理第 {page_num + 1} 页时出错: {str(page_error)}")
                writer.add_page(reader.pages[page_num])
                continue

        # 确保目标目录存在
        os.makedirs(os.path.dirname(dest_path), exist_ok=True)

        # 保存修正后的PDF
        with open(dest_path, 'wb') as output_file:
            writer.write(output_file)

        return True

    except Exception as e:
        app.logger.error(f"PDF校准失败: {str(e)}")
        raise e


def get_error_priority(error_type):
    """
    获取错误类型的处理优先级
    """
    priorities = {
        '页边距': 1,  # 先处理页边距
        '字体': 2,  # 再处理字体
        '行距': 3,  # 然后是行距
        '对齐': 4,  # 最后处理对齐
    }
    return priorities.get(error_type, 999)  # 未知错误类型放最后


def apply_pdf_correction(canvas, error, page_width, page_height, registered_fonts):
    """
    应用具体的PDF修正
    """
    try:
        error_type = error.get('type', '')
        description = error.get('description', '')

        app.logger.debug(f"应用修正: 类型={error_type}, 描述={description}")

        if error_type == '字体':
            # 根据论文格式规范处理字体
            if '标题' in description:
                if 'SimHei' in registered_fonts:
                    canvas.setFont('SimHei', 16)  # 黑体三号
            elif '摘要' in description:
                if 'SimSun' in registered_fonts:
                    canvas.setFont('SimSun', 14)  # 宋体四号
            elif '正文' in description:
                if 'SimSun' in registered_fonts:
                    canvas.setFont('SimSun', 12)  # 宋体小四号

        elif error_type == '页边距':
            # 按照论文格式规范设置页边距
            top_margin = 72 * 1.0  # 上边距1英寸
            bottom_margin = 72 * 1.0  # 下边距1英寸
            left_margin = 72 * 1.25  # 左边距1.25英寸
            right_margin = 72 * 1.0  # 右边距1英寸

            # 绘制边距指示线（可选）
            canvas.setStrokeColorRGB(0.9, 0.9, 0.9)  # 浅灰色
            canvas.rect(
                left_margin, bottom_margin,
                page_width - (left_margin + right_margin),
                page_height - (top_margin + bottom_margin)
            )

        elif error_type == '行距':
            # 处理行距（1.5倍行距）
            line_height = 18  # 1.5倍行距约等于18pt
            y = page_height - 72
            canvas.setStrokeColorRGB(0.95, 0.95, 0.95)  # 更浅的灰色
            while y > 72:
                canvas.line(72, y, page_width - 72, y)
                y -= line_height

        elif error_type == '对齐':
            # 处理文本对齐
            if '居中' in description:
                canvas.drawCentredString(page_width / 2, page_height / 2, "")
            elif '两端对齐' in description:
                # PDF不直接支持两端对齐，这里只能做标记
                pass

    except Exception as e:
        app.logger.error(f"应用PDF修正时出错: {str(e)}")
        raise e


def calibrate_word_doc(src_path, dest_path, analysis_data):
    doc = Document(src_path)

    # 全局样式设置
    set_global_styles(doc, analysis_data)

    # 按错误类型处理
    for error in analysis_data.get('errors', []):
        handle_single_error(doc, error)

    doc.save(dest_path)


def set_global_styles(doc, data):
    # 设置页边距（示范值）
    section = doc.sections
    section.top_margin = Cm(2.5)
    section.bottom_margin = Cm(2.5)
    section.left_margin = Cm(3)
    section.right_margin = Cm(3)

    # 设置默认字体
    style = doc.styles['Normal']
    font = style.font
    font.name = '宋体'
    font.size = Pt(12)


def handle_single_error(doc, error):
    try:
        if error['type'] == '字体':
            # 示例：处理一级标题字体
            if '黑体三号' in error['description']:
                for paragraph in doc.paragraphs:
                    if paragraph.style.name == 'Heading 1':
                        paragraph.style.font.name = '黑体'
                        paragraph.style.font.size = Pt(16)

        elif error['type'] == '段落':
            # 设置首行缩进和行距
            if '首行缩进' in error['description']:
                for paragraph in doc.paragraphs:
                    paragraph.paragraph_format.first_line_indent = Cm(0.74)  # 2字符
                    paragraph.paragraph_format.line_spacing = Pt(22)

    except Exception as e:
        app.logger.warning(f"错误处理失败: {error} - {str(e)}")


if __name__ == '__main__':
    os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
    os.makedirs(app.config['CALIBRATED_FOLDER'], exist_ok=True)
    app.run(port=5000)
